<template>
  <div id="container"></div>
</template>
<script setup>
import * as THREE from 'three';
import { onMounted } from 'vue';
import { lonlat2xyz } from '@/three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';

var R = 1000;
var width = window.innerWidth;
var height = window.innerHeight;
var scene, camera, renderer, controls, scaneMaterial;
var textureLoader = new THREE.TextureLoader();
var loader = new THREE.FileLoader();
loader.setResponseType('json');

onMounted(() => {
  init();
  render();
  createEarth();
  createWorldLine();
  createDataPoint();
  createScanning();
});

function init() {
  scene = new THREE.Scene();
  scene.add(new THREE.AmbientLight(0xffffff, 1));

  renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(width, height);
  document.getElementById('container').appendChild(renderer.domElement);

  camera = new THREE.PerspectiveCamera(40, width / height, 1, 100000);
  camera.position.y = 1500;
  camera.position.z = -3000;

  controls = new OrbitControls(camera, renderer.domElement);
  controls.enablePan = false;
  controls.enableDamping = true;
  // controls.minDistance = 120;
  // controls.maxDistance = 1000;
}

function createEarth() {
  var geometry = new THREE.SphereGeometry(R, 100, 100);
  var geometry2 = new THREE.SphereGeometry(R + 1, 100, 100);
  var texture = textureLoader.load('images/earth.png');
  texture.minFilter = THREE.LinearFilter;
  var material = new THREE.MeshLambertMaterial({
    map: texture
  });
  scene.add(new THREE.Mesh(geometry, material));
  scene.add(new THREE.Mesh(geometry2, atmosphericMaterial()));
}

function render() {
  controls.update();
  requestAnimationFrame(render);
  renderer.render(scene, camera);
  if (scaneMaterial) {
    scaneMaterial.uniforms.time.value = scaneMaterial.uniforms.time.value - 0.01;
  }
}

function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
}

window.addEventListener('resize', onWindowResize, false);

function createWorldLine() {
  var group = new THREE.Group();
  loader.load('json/world.json', res => {
    res.features.forEach(country => {
      if (country.geometry.type === 'Polygon') {
        country.geometry.coordinates = [country.geometry.coordinates];
      }
      country.geometry.coordinates.forEach(polygon => {
        polygon.forEach(arr => {
          group.add(createLine(arr));
        });
      });
    });
    scene.add(group);
  });
}
function createLine(arr) {
  var color = 0x2f3548;
  var material = new THREE.LineBasicMaterial({ color });
  var points = arr.map(item => {
    var coord = lonlat2xyz(item, R);
    return new THREE.Vector3(coord.x, coord.y, coord.z);
  });
  var geometry = new THREE.BufferGeometry().setFromPoints(points);
  return new THREE.LineLoop(geometry, material);
}

function createDataPoint() {
  var group = new THREE.Group();
  var data = {};
  loader.load('json/data.json', res => {
    res.features.forEach(item => {
      var size = point_size(item.properties.level);
      var color = point_color(item.properties.depth);
      var key = `${color}-${size}`;
      if (data[key]) {
        data[key].push(item);
      } else {
        data[key] = [item];
      }
    });
    var points = Object.values(data);
    var options = Object.keys(data);
    points.forEach((arr, i) => {
      group.add(createPoints(arr, options[i]));
    });
    scene.add(group);
  });
}

function point_color(depth) {
  const depthColors = ['#D60352', '#F86615', '#F86615', '#F86615', '#F86615'];
  var index = ~~((depth / 120) * depthColors.length) % depthColors.length;
  return depthColors[index];
}

function point_size(level) {
  if (level < 7) {
    return level / 2;
  }
  return level;
}

function createPoints(arr, str) {
  const vertices = [];
  arr.forEach(item => {
    var coord = lonlat2xyz(item.geometry.coordinates, R + 1);
    vertices.push(coord.x, coord.y, coord.z);
  });
  const geometry = new THREE.BufferGeometry();
  geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
  var texture = textureLoader.load('images/dot.png');
  var size = Number(str.split('-')[1]) * 2;
  var color = new THREE.Color(str.split('-')[0]);
  const material = new THREE.PointsMaterial({
    color,
    size,
    alphaMap: texture,
    depthWrite: false,
    opacity: 0.8,
    transparent: true,
    sizeAttenuation: false
  });
  return new THREE.Points(geometry, material);
}
function createScanning() {
  var vertexShader = `
    varying vec2 vUv;
    void main(){
      vUv = uv;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  `;
  var fragmentShader = `
    varying vec2 vUv;
    uniform float time;
    uniform vec3 color;
    uniform float opacity;
    uniform sampler2D scaneTexture;
    void main(){
      vec4 textureColor = texture2D(scaneTexture, vec2(vUv.x, vUv.y + time));
      gl_FragColor = vec4( vec3(textureColor) * color, textureColor.a * opacity);
    }
  `;
  var canvas = document.createElement('canvas');
  canvas.width = 1024;
  canvas.height = 1024;
  var ctx = canvas.getContext('2d');
  for (var i = 20; i > 0; i--) {
    ctx.beginPath();
    ctx.rect(0, 10 * (20 - i), 1024, 2);
    ctx.fillStyle = `rgba(255, 255, 255, ${i / 20})`;
    ctx.fill();
  }
  var scaneTexture = new THREE.Texture(canvas);
  scaneTexture.wrapT = THREE.RepeatWrapping;
  scaneTexture.wrapS = THREE.RepeatWrapping;
  scaneTexture.minFilter = THREE.LinearFilter;
  scaneTexture.needsUpdate = true;
  scaneMaterial = new THREE.ShaderMaterial({
    uniforms: {
      time: {
        type: 'f',
        value: 0
      },
      color: {
        type: 'c',
        value: new THREE.Color('#D60352')
      },
      scaneTexture: {
        value: scaneTexture
      },
      opacity: {
        type: 'f',
        value: 1
      }
    },
    vertexShader: vertexShader,
    fragmentShader: fragmentShader,
    transparent: true,
    depthTest: false,
    blending: THREE.AdditiveBlending
  });
  var center = [116.318031, 39.951893];
  var scaneMesh = new THREE.Mesh(new THREE.SphereGeometry(1000, 128, 128), scaneMaterial);
  scaneMesh.renderOrder = 20;
  scaneMesh.setRotationFromEuler(
    new THREE.Euler(0, (center[0] / 180) * Math.PI, ((center[1] - 90) / 180) * Math.PI, 'XYZ')
  );
  scene.add(scaneMesh);
}
function atmosphericMaterial() {
  return new THREE.ShaderMaterial({
    uniforms: {
      power: { type: 'f', value: 3 },
      coefficient: { type: 'f', value: 0.8 },
      glowColor: { type: 'c', value: new THREE.Color(0xeeeeee) }
    },
    vertexShader: `
        varying vec3	vVertexWorldPosition;
        varying vec3	vVertexNormal;
        varying vec4	vFragColor;
        void main(){
          vVertexNormal	= normalize(normalMatrix * normal);
          vVertexWorldPosition	= (modelMatrix * vec4(position, 1.0)).xyz;
          gl_Position	= projectionMatrix * modelViewMatrix * vec4(position, 1.0);
        }
      `,
    fragmentShader: `
        uniform vec3	glowColor;
        uniform float	coefficient;
        uniform float	power;
        varying vec3	vVertexNormal;
        varying vec3	vVertexWorldPosition;
        varying vec4	vFragColor;
        void main(){
          vec3 worldCameraToVertex= vVertexWorldPosition - cameraPosition;
          vec3 viewCameraToVertex	= (viewMatrix * vec4(worldCameraToVertex, 0.0)).xyz;
          viewCameraToVertex	= normalize(viewCameraToVertex);
          float intensity		= pow(coefficient + dot(vVertexNormal, viewCameraToVertex), power);
          gl_FragColor		= vec4(glowColor, intensity);
        }
      `,
    blending: THREE.NormalBlending,
    transparent: true,
    depthWrite: false
  });
}
</script>
